Expand description

docs.rs crates.io loicense gitlab build

Overview

A library crate providing a dependency-free, pure rust implementation of the cashaddr codec.

Features

  • Trait-based interfaces for transcoding arbitrary sequence of bytes to/from cashaddr strings
  • Generalized interface supporting all standard and many non-standard use-cases
  • Convenience methods for succinct expression of common conversion parameters
  • Custom hash types
  • Arbitrary prefixes
  • Elided prefix
  • Descriptive error types
  • Payload struct for encapsulating parsed cashaddr payload and hash type

About the Codec

Cashaddr is a base32-based encoding scheme designed to encode a hash digest and hash type which describes the use case for the hash. The hash is an arbitrary sequence of either 20, 24, 28, 32, 40, 48, 56, or 64 bytes. The cashaddr format represents this information as a string which consists of 2 parts: an arbitrary user-defined prefix, and a base32-encoded representation of the hash, hash type, hash length, and a checksum which checks both the hash payload and the user prefix. For details, see the cashaddr spec

Attribution

Most of the codec algorithm logic was copied from bitcoincash-addr. This crate seeks to improve on bitcoincash-addr by providing a more generalized and ergonomic user interface, adding support for arbitrary prefixes, and reducing scope to only matters directly related the cashaddr codec itself (base58check codec removed from scope).

Usage

Encoding

Encoding a sequence of bytes into a cashaddr string is acheived via the CashEnc trait. This trait’s methods are used to encode the bytes sequence, and is implemented for all types which implement [AsRef<[u8]>]

use cashaddr::CashEnc;
let payload = b"\xf5\xbfH\xb3\x97\xda\xe7\x0b\xe8+<\xcaG\x93\xf8\xeb+l\xda\xc9";

// encode the payload bytes as a p2sh cashaddr, using "bchtest" as the prefix
let cashaddr = "bchtest:pr6m7j9njldwwzlg9v7v53unlr4jkmx6eyvwc0uz5t";
assert_eq!(payload.encode_p2sh("bchtest").unwrap(), cashaddr);

// encode the payload bytes as a p2pkh cashaddr, using "bitcoincash" as the prefix
let cashaddr = "bitcoincash:qr6m7j9njldwwzlg9v7v53unlr4jkmx6eylep8ekg2";
assert_eq!(payload.encode_p2pkh("bitcoincash").unwrap(), cashaddr);

// arbitrary prefixes are supported
let cashaddr = "foobar:qr6m7j9njldwwzlg9v7v53unlr4jkmx6eyde268tla";
assert_eq!(payload.encode_p2pkh("foobar").unwrap(), cashaddr);

Incorrect payload length is detected and captured during encoding

use cashaddr::{CashEnc, EncodeError};
let payload = b"\xf5\xbfH\xb3\x97\xda\xe7\x0b\xe8+<\xcaG\x93\xf8\xeb+l\xda\xc9t";
match payload.encode_p2pkh("someprefix") {
    Err(EncodeError::IncorrectPayloadLen(21)) => (), // pass
    Err(EncodeError::IncorrectPayloadLen(_)) => panic!(
        "Detected incorrect payload length, but failed to capture the correct actual input len"
    ),
    Err(e) => panic!("Detected unexpected error {}", e),
    Ok(_) => panic!("Failed to detect incorrect payload length"),
}

Decoding

Decoding a cashaddr str to a binary payload is acheived via the Payload type which encapsulates the payload itself and the detected hash type. Parsing is provided by the FromStr trait

use cashaddr::{Payload, HashType, DecodeError};

let EXPECTED_PAYLOAD = b"\xf5\xbfH\xb3\x97\xda\xe7\x0b\xe8+<\xcaG\x93\xf8\xeb+l\xda\xc9";

// Use parse() to decode a P2PKH cashaddr string to a `Payload`
let cashaddr = "bitcoincash:qr6m7j9njldwwzlg9v7v53unlr4jkmx6eylep8ekg2";
let payload: Payload = cashaddr.parse().unwrap();
assert_eq!(payload.as_ref(), EXPECTED_PAYLOAD);
assert_eq!(payload.hash_type(), HashType::P2PKH);

// Use parse() to decode a P2SH cashaddr string to a `Payload`
let cashaddr = "bitcoincash:pr6m7j9njldwwzlg9v7v53unlr4jkmx6eyguug74nh";
let payload: Payload = cashaddr.parse().unwrap();
assert_eq!(payload.as_ref(), EXPECTED_PAYLOAD);
assert_eq!(payload.hash_type(), HashType::P2SH);

// arbitrary prefix are supported in decoding
let cashaddr = "foobar:qr6m7j9njldwwzlg9v7v53unlr4jkmx6eyde268tla";
let payload: Payload = cashaddr.parse().unwrap();
assert_eq!(payload.as_ref(), EXPECTED_PAYLOAD);
assert_eq!(payload.hash_type(), HashType::P2PKH);

// Decoding is canse insensitive in the second part of the cashaddr
let cashaddr = "foobar:qr6M7j9NJLdwwzLG9v7v53UNlr4jkmX6eyDe268tla";
let payload: Payload = cashaddr.parse().unwrap();
assert_eq!(payload.as_ref(), EXPECTED_PAYLOAD);
assert_eq!(payload.hash_type(), HashType::P2PKH);

// Decoding checks that the checksum is valid
// This char was changed to "8" -------↓
let cashaddr = "foobar:qr6M7j9NJLdwwzLG8v7v53UNlr4jkmX6eyDe268tla";
match cashaddr.parse::<Payload>() {
    Err(DecodeError::ChecksumFailed(_)) => (),
    _ => panic!("Failed to detect corrupt cashaddr checksum"),
}

Structs

Representation of a parsed cashaddr payload (i.e. the hash) and a hash type.

Enums

Error type describing something that went wrong during decoding a cashaddr string.
Error type describing something that went wrong during enoding a sequence of u8 into a cashaddr String
Type of hash payload. Currently, either P2PKH or P2SH, but in the furture more variants may be added if they are standardized by Bitcoin Cash developers.

Constants

The set of allowed hash lengths for cashaddr encoding.
The cashaddr character set for encoding

Traits

Encode a sequence of bytes (u8) as a cashaddr string. This trait is implemented for all types implementing AsRef<[u8]> where the reference value is a slice of u8 representing the hash payload bytes.